using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using ChessKit.ViewModel;
using Action = System.Action;

namespace EmraldClient
{
  public class GameViewModel : MyScreen
  {
    private int _moveNumber;
    private Stopwatch _timer;

    public Problem Problem { get; private set; }
    public string UserName { get; private set; }

    #region ' Board Property '

    public BoardViewModel Board
    {
      get { return _board; }
      set
      {
        if (_board == value) return;
        _board = value;
        NotifyOfPropertyChange(() => Board);
      }
    }

    private BoardViewModel _board;

    #endregion

    #region ' IsLoading Property (change notification) '

    public bool IsLoading
    {
      get { return _isLoading; }
      set
      {
        if (_isLoading == value) return;
        _isLoading = value;
        NotifyOfPropertyChange(() => IsLoading);
      }
    }

    private bool _isLoading;

    #endregion

    #region ' Status Property (change notification) '

    public string Status
    {
      get { return _status; }
      set
      {
        if (_status == value) return;
        _status = value;
        NotifyOfPropertyChange(() => Status);
      }
    }

    private string _status;

    #endregion

    public GameViewModel()
    {
      _board = new BoardViewModel();
      _board.LoadFen(Problem.Position);
    }

    public GameViewModel(Problem problem, string userName)
    {
      _board = new BoardViewModel();
      _board.LoadFen(problem.Position);
      _board.Moved += BoardDataOnMoved;
      Problem = problem;
      UserName = userName;
    }

    protected override void OnViewLoaded(object view)
    {
      base.OnViewLoaded(view);

      if (DesignerProperties.GetIsInDesignMode((DependencyObject) view)) return;

      Task.Factory.StartNew(() => Thread.Sleep(1000), 
        CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).
        ContinueWith(t => Move(),
                     CancellationToken.None,
                     TaskContinuationOptions.OnlyOnRanToCompletion,
                     TaskScheduler.FromCurrentSynchronizationContext());

      UpdateSmile();

      Task.Factory.StartNew(() => Thread.Sleep((int)(Problem.Ex.TotalMilliseconds*3)),
        CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).
        ContinueWith(t => Sleeping(),
                     CancellationToken.None,
                     TaskContinuationOptions.OnlyOnRanToCompletion,
                     TaskScheduler.FromCurrentSynchronizationContext());
    }

    private void Move()
    {
      if (_moveNumber == 0) _timer = Stopwatch.StartNew();
      var move = Problem.Moves[_moveNumber++];
      _board.Move(move.From, move.To, move.ProposedPromotion);

      NotifyOfPropertyChange(() => LastMove);

    }
    private void BoardDataOnMoved(object sender, MoveEventArgs e)
    {
      var expected = Problem.Moves[_moveNumber++];
      var succ = e.Move == expected;
      Problem.IsSolved = succ;
      UpdateSmile();

      if (succ && _moveNumber < Problem.Moves.Count)
      {
        Dispatcher.CurrentDispatcher.BeginInvoke(
          DispatcherPriority.ApplicationIdle, new Action(Move));
      }
      else
      {
        Problem.Time = TimeSpan.FromSeconds(
          _timer.Elapsed.TotalSeconds / _moveNumber * 2);

        OnDone(EventArgs.Empty);
      }
    }
    private void Sleeping()
    {
      if (Problem.IsSolved == null)
      {
        Smile = "Sleeping";
      }
    }

    private void UpdateSmile()
    {
      if (Problem.IsSolved == null)
      {
        Smile = "Neutral" + Rnd.Next(1, 5);
      }
      else if (Problem.IsSolved == true)
      {
        Smile = "Positive" + Math.Min(_moveNumber / 2, 3);
        LoosingStreak = 0;
      }
      else if (Problem.IsSolved == false)
      {
        Smile = "Negative" + Math.Min(++LoosingStreak, 3);
      }
    }

    private static int LoosingStreak = 0;

    private static readonly Random Rnd = new Random();

    #region ' Smile Property (change notification) '

    public string Smile
    {
      get { return _smile; }
      set
      {
        if (_smile == value) return;
        _smile = value;
        NotifyOfPropertyChange(() => Smile);
      }
    }

    private string _smile;

    #endregion

    public string LastMove
    {
      get
      {
        var index = _moveNumber - 1;
        var list = Problem.Moves;
        return index >= 0 && index < list.Count ? list[index].ToString() : "";
      }
    }
  }
}